﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SAEA_TestServer.Server
{
    public class SAEA_Server
    {
        private readonly Socket socket_Server;
        private readonly EndPoint socket_endPoint;
        private Semaphore maxClientCountController;
        private const int maxClientCount = 100;
        private int AsnyBufferLength = 64;
        private Dictionary<string, SocketAsyncEventArgs> dicSAEA = null;
        private SAEA_AsnySocketPool SAEAPool;

        public delegate void OnEventCompletedHanlder(string MassageArray);
        public event OnEventCompletedHanlder OnReceiveCompletedEvent;
        public event OnEventCompletedHanlder OnSendCompletedEvent;
        public event OnEventCompletedHanlder OnConnectedEvent;
        public event OnEventCompletedHanlder OnDisconnectEvent;
        public event OnEventCompletedHanlder OnNotConnectEvent;


        public SAEA_Server(string ip,int port)
        {
            socket_Server = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
            IPAddress ipAddress = IPAddress.Parse(ip);
            socket_endPoint = new IPEndPoint(ipAddress,port);
            //连接数控制器初始化
            maxClientCountController = new Semaphore(maxClientCount, maxClientCount);
            Init();
        }

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        private void Init()
        {
            Console.WriteLine("初始化异步对象100个");
            SocketAsyncEventArgs rwEventArgs;
            //初始化对象池
            SAEAPool = new SAEA_AsnySocketPool(maxClientCount);
            //初始化对象池字典
            dicSAEA = new Dictionary<string, SocketAsyncEventArgs>();
            //遍历生成异步对象，并给每个异步对象的结束事件添加函数
            for (int i = 0; i < maxClientCount; i++)
            {
                rwEventArgs = new SocketAsyncEventArgs();
                //给每个异步对象添加结束函数
                rwEventArgs.Completed += OnSAEACompleteEvent;
                //给每个异步对象添加连接对象管理类
                rwEventArgs.UserToken = new AsnyUserToken();
                //给每个异步对象添加缓存池
                rwEventArgs.SetBuffer(new byte[AsnyBufferLength],0,AsnyBufferLength );
                //将生成的异步对象添加到对象池中
                SAEAPool.Push(rwEventArgs);
            }
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        public void Start()
        {
            Console.WriteLine("启动服务器...");
            socket_Server.Bind(socket_endPoint);
            socket_Server.Listen(maxClientCount);
            //开始接收客户端连接
            StartAccept(null);
        }

        /// <summary>
        /// 开始接收异步对象
        /// </summary>
        /// <param name="saea"></param>
        public void StartAccept(SocketAsyncEventArgs saea)
        {
            Console.WriteLine("开始接收客户端...");
            if (saea == null)
            {
                //如果是空，则此对象第一次连接，添加连接回调函数
                saea = new SocketAsyncEventArgs();
                saea.Completed += OnSAEAConnectCompleteEvent;
            }
            else
            {
                //如果不是，清空socket
                saea.AcceptSocket = null;
            }
            //检测当前连接数是否超过最大数量，没有则接收此对象的连接
            maxClientCountController.WaitOne();
            //判断当前连接对象是否是异步连接，true则是异步连接，false则表示同步完成
            bool willRaiseEvent = socket_Server.AcceptAsync(saea);
            if (!willRaiseEvent)
            {
                //已经同步接收完成，触发回调函数
                HandleAccept(saea);
            }
        }

        /// <summary>
        /// 发送消息函数
        /// </summary>
        /// <param name="saea"></param>
        public void SendMessage(MsgModel model)
        {
            //检测客户端是否存在
            if (!dicSAEA.ContainsKey(model.ipAddress))
            {
                OnNotConnectEvent?.Invoke("不存在"+ model.ipAddress+"的客户端");
                return;
            }
            SocketAsyncEventArgs saea = dicSAEA[model.ipAddress];
            AsnyUserToken token = saea.UserToken as AsnyUserToken;
            if(saea == null) return;
            //开始发送消息
            byte[] sendbuffer = Encoding.Unicode.GetBytes(model.massage);
            saea.SetBuffer(sendbuffer,0,sendbuffer.Length);
            bool willRaiseEvent = token.UserSocket.SendAsync(saea);
            if (!willRaiseEvent)
            {
                //同步发送成功，执行回调函数
                OnSendComplete(saea);
            }
        }

        #region 回调事件
        /// <summary>
        /// 处理连接成功函数，异步和同步成功后都会调用此函数
        /// </summary>
        /// <param name="saea">连接成功回调</param>
        private void HandleAccept(SocketAsyncEventArgs saea)
        {
            if (saea.SocketError != SocketError.Success) return;
            Console.WriteLine("有新的客户端连接到服务器");
            OnConnectedEvent?.Invoke("连接成功...");
            //从池子中取出一个异步对象
            SocketAsyncEventArgs e = SAEAPool.Pop();
            AsnyUserToken token = e.UserToken as AsnyUserToken;
            //设置异步对象中的连接参数
            token.UserSocket = saea.AcceptSocket;
            saea.UserToken = token;
            //string ipRemote = token.UserSocket.RemoteEndPoint.ToString();
            //string ip = token.UserSocket.RemoteEndPoint.ToString();

            //连接成功后立刻发送消息给客户端
            SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
            sendArgs.RemoteEndPoint = token.UserSocket.RemoteEndPoint;
            sendArgs.Completed += OnSAEACompleteEvent;
            sendArgs.UserToken = saea.UserToken;
            //将新连接的对象添加到字典中
            dicSAEA.Add(token.UserSocket.RemoteEndPoint.ToString(),sendArgs);
            //MsgModel model = new MsgModel(sendArgs.RemoteEndPoint.ToString(),"connect success");
            //SendMessage(model);
            
            bool willRaiseEvent = saea.AcceptSocket.ReceiveAsync(e);
            if (!willRaiseEvent)
            {
                //同步接收完成执行回调
                OnReceiveComplete(e);
            }
        }


        /// <summary>
        /// 事件结束执行函数
        /// </summary>
        /// <param name="sender">发送对象</param>
        /// <param name="saea">当前事件对象</param>
        public void OnSAEACompleteEvent(object sender, SocketAsyncEventArgs saea)
        {
            //判断当前操作类型
            switch (saea.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    //异步接收完成执行回调
                    OnReceiveComplete(saea);
                    break;
                case SocketAsyncOperation.Send:
                    OnSendComplete(saea);
                    break;
                default:
                    Console.WriteLine("异常！该操作类型不是接收也不是发送");
                    break;
            }
        }


        public void OnSAEAConnectCompleteEvent(object sender, SocketAsyncEventArgs saea)
        {
            //连接成功，执行连接成功函数
            HandleAccept(saea);
        }

        /// <summary>
        /// 接受到新的消息,异步和同步成功后都会调用此函数
        /// </summary>
        /// <param name="saea"></param>
        public void OnReceiveComplete(SocketAsyncEventArgs saea)
        {
            if (saea.SocketError != SocketError.Success) return;
            //获取到接收对象
            AsnyUserToken token = saea.UserToken as AsnyUserToken;
            //获取到消息长度
            int lengthbuffer = saea.BytesTransferred;
            string ipAddress = token.UserSocket.RemoteEndPoint.ToString();
            if (lengthbuffer <= 0)
            {
                //服务器断开连接，执行断开连接事件
                CloseClientSocket(saea);
            }
            else
            {
                //todo 此处考虑使用缓存池处理
                byte[] receiveBuffer = saea.Buffer;
                byte[] buffer = new byte[lengthbuffer];
                //取出有效长度的字节数组
                Buffer.BlockCopy(receiveBuffer,0,buffer,0,lengthbuffer);
                //开启下一次异步接收函数
                bool willRaiseEvent = token.UserSocket.ReceiveAsync(saea);
                if (!willRaiseEvent)
                {
                    OnReceiveComplete(saea);
                }
                //消息处理，委托?表示判断不为空则执行该委托
                OnReceiveCompletedEvent?.Invoke("接收到新的消息，开始处理本次消息");
            }
        }

        /// <summary>
        /// 发送成功回调
        /// </summary>
        /// <param name="saea"></param>
        public void OnSendComplete(SocketAsyncEventArgs saea)
        {
            //AsnyUserToken token = saea.UserToken as AsnyUserToken;
            //byte[] sendBuffer = saea.Buffer;
            Console.WriteLine("发送成功");
        }


        /// <summary>
        /// 关闭当前异步对象
        /// </summary>
        /// <param name="saea"></param>
        public void CloseClientSocket(SocketAsyncEventArgs saea)
        {

            Console.WriteLine("客户端断开连接...");
            AsnyUserToken token = saea.UserToken as AsnyUserToken;
            try
            {
                token.UserSocket.Shutdown(SocketShutdown.Send);
            }
            catch (Exception) {}
            //从当前字典中移除掉线的异步对象
            dicSAEA.Remove(token.UserSocket.RemoteEndPoint.ToString());
            token.UserSocket.Close();
            //释放连接数
            maxClientCountController.Release();
            //添加到对象池
            SAEAPool.Push(saea);
        }

        #endregion
    }
}
